/**
 * Project Name:fire-industry-DM-center
 * File Name:MessageConverter.java
 * Package Name:com.firestone.config.mvc
 * Date:2018年3月29日下午6:01:27
 *
*/

package com.firestone.config.mvc;

import java.io.IOException;
import java.nio.charset.Charset;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.Assert;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.type.TypeFactory;

/**
 * @Description: TODO(消息转换器，将返回消息中的 null 转成空字符串)
 * @author CAIYJ
 * @date 2018年3月29日 下午6:01:27
 */
@Configuration
public class MessageConverter extends AbstractHttpMessageConverter<Object> {
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private ObjectMapper objectMapper = new ObjectMapper();

    private boolean prefixJson = false;

    public MessageConverter() {
        super(new MediaType("application", "json", DEFAULT_CHARSET));
    }

    public void setObjectMapper(ObjectMapper objectMapper) {
        Assert.notNull(objectMapper, "'objectMapper' must not be null");
        this.objectMapper = objectMapper;
    }

    public void setPrefixJson(boolean prefixJson) {
        this.prefixJson = prefixJson;
    }

    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        JavaType javaType = getJavaType(clazz);
        return (this.objectMapper.canDeserialize(javaType))
                && (canRead(mediaType));
    }

    protected JavaType getJavaType(Class<?> clazz) {
        return TypeFactory.defaultInstance().constructType(clazz);
    }

    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return (this.objectMapper.canSerialize(clazz)) && (canWrite(mediaType));
    }

    protected boolean supports(Class<?> clazz) {
        throw new UnsupportedOperationException();
    }

    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        JavaType javaType = getJavaType(clazz);
        try {
            return this.objectMapper.readValue(inputMessage.getBody(),
                    javaType);
        } catch (JsonParseException ex) {
            throw new HttpMessageNotReadableException(
                    "Could not read JSON: " + ex.getMessage(), ex);
        }
    }

    @SuppressWarnings("deprecation")
    protected void writeInternal(Object o, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {

        JsonEncoding encoding = getEncoding(
                outputMessage.getHeaders().getContentType());
        SerializerProvider serializerProvider = this.objectMapper
                .getSerializerProvider();
        serializerProvider.setNullValueSerializer(new JsonSerializer<Object>() {

            @Override
            public void serialize(Object arg0, JsonGenerator arg1,
                    SerializerProvider arg2)
                            throws IOException, JsonProcessingException {
                arg1.writeObject("");
            }

        });

        JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory()
                .createJsonGenerator(outputMessage.getBody(), encoding);
        try {
            if (this.prefixJson) {
                jsonGenerator.writeRaw("{} && ");
            }
            this.objectMapper.writeValue(jsonGenerator, o);
        } catch (JsonGenerationException ex) {
            throw new HttpMessageNotWritableException(
                    "Could not write JSON: " + ex.getMessage(), ex);
        }
    }

    private JsonEncoding getEncoding(MediaType contentType) {
        if ((contentType != null) && (contentType.getCharset() != null)) {
            Charset charset = contentType.getCharset();
            for (JsonEncoding encoding : JsonEncoding.values()) {
                if (charset.name().equals(encoding.getJavaName())) {
                    return encoding;
                }
            }
        }
        return JsonEncoding.UTF8;
    }

}
